1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
use crate::sketchbook::ids::{LayoutId, UninterpretedFnId, VarId};
use crate::sketchbook::model::ModelState;
use crate::sketchbook::Manager;
/// Methods for safely generating valid instances of identifiers for the current `ModelState`.
impl ModelState {
/// Generate valid `VarId` that's currently not used by any variable in this `ModelState`.
///
/// First, the given `ideal_id` or its transformation by replacing invalid characters are tried.
/// If they are both invalid (non-unique), a numerical identifier is added at the end.
/// By specifying `start_index`, the index search starts directly at that number (e.g., when
/// ideal ID is "var" and start index is 3, search for ID starts with "var_3", "var_4", ...)
///
/// **Warning:** Do not use this to pre-generate more than one id at a time, as the process
/// is deterministic and might generate the same IDs. Always generate an Id, add that variable to
/// the model, and then repeat for other variables.
pub fn generate_var_id(&self, ideal_id: &str, start_index: Option<usize>) -> VarId {
self.generate_id(
ideal_id,
&(Self::is_valid_var_id),
self.num_vars(),
start_index,
)
}
/// Generate valid `LayoutId` that's currently not used by layouts in this `ModelState`.
///
/// First, the given `ideal_id` or its transformation by replacing invalid characters are tried.
/// If they are both invalid (non-unique), a numerical identifier is added at the end.
/// By specifying `start_index`, the index search starts directly at that number (e.g., when
/// ideal ID is "l" and start index is 3, search for ID starts with "l_3", "l_4", ...)
///
/// **Warning:** Do not use this to pre-generate more than one id at a time, as the process
/// is deterministic and might generate the same IDs. Always generate an Id, add that layout to
/// the model, and then repeat for other layouts.
pub fn generate_layout_id(&self, ideal_id: &str, start_index: Option<usize>) -> LayoutId {
self.generate_id(
ideal_id,
&(Self::is_valid_layout_id),
self.num_layouts(),
start_index,
)
}
/// Generate valid `UninterpretedFnId` that's currently not used by uninterpreted_fns in this `ModelState`.
///
/// First, the given `ideal_id` or its transformation by replacing invalid characters are tried.
/// If they are both invalid (non-unique), a numerical identifier is added at the end.
/// By specifying `start_index`, the index search starts directly at that number (e.g., when
/// ideal ID is "fn" and start index is 3, search for ID starts with "fn_3", "fn_4", ...)
///
/// **Warning:** Do not use this to pre-generate more than one id at a time, as the process
/// is deterministic and might generate the same IDs. Always generate an Id, add that fn to
/// the model, and then repeat for other fns.
pub fn generate_uninterpreted_fn_id(
&self,
ideal_id: &str,
start_index: Option<usize>,
) -> UninterpretedFnId {
self.generate_id(
ideal_id,
&(Self::is_valid_uninterpreted_fn_id),
self.num_uninterpreted_fns(),
start_index,
)
}
}
#[cfg(test)]
mod tests {
use crate::sketchbook::ids::{LayoutId, VarId};
use crate::sketchbook::model::ModelState;
#[test]
fn test_var_id_generating() {
let model =
ModelState::new_from_vars(vec![("a", "name"), ("b", "name"), ("c", "name")]).unwrap();
assert_eq!(model.num_vars(), 3);
// name slice that is a valid identifier as is
let var_name_1 = "d";
assert_eq!(
model.generate_var_id(var_name_1, Some(1)),
VarId::new("d_1").unwrap()
);
// name that is not a valid identifier as it contains various invalid characters
let var_name_2 = "-d ??)& ";
assert_eq!(
model.generate_var_id(var_name_2, Some(1)),
VarId::new("d_1").unwrap()
);
// name that is already used in the network
let var_name_3 = "a";
// result will contain an numerical index in the end
assert_eq!(
model.generate_var_id(var_name_3, None),
VarId::new("a_1").unwrap()
);
// name that starts with a number - will be cleaned and prefixed with "v_"
let var_name_4 = "4ab??";
// result will contain an numerical index in the end
assert_eq!(
model.generate_var_id(var_name_4, None),
VarId::new("x_4ab").unwrap()
);
}
#[test]
fn test_layout_id_generating() {
let mut model = ModelState::new_empty();
let layout_id = LayoutId::new("l").unwrap();
let default_layout_id = ModelState::get_default_layout_id();
model.add_layout_simple(layout_id, "name").unwrap();
assert_eq!(model.num_layouts(), 2);
// expected result for all the following IDs will be the same
let expected = LayoutId::new("l_1").unwrap();
// name slice that is a valid identifier as is
let name_1 = "l";
assert_eq!(model.generate_layout_id(name_1, None), expected);
// name that is not a valid identifier as it contains various invalid characters
let name_2 = "%%%%l &)";
assert_eq!(model.generate_layout_id(name_2, None), expected);
// add new layout
let layout_id = LayoutId::new("ll").unwrap();
model
.add_layout_copy(layout_id, "name", &default_layout_id)
.unwrap();
// try generate ID for the same layout again - the result will have numerical index appended
// however, this time we cant just add index 0 because the result would not be unique
let name_3 = "ll";
let expected = LayoutId::new("ll_1").unwrap();
// search for unused index is incremental, starting at 0 (until valid index 1 is found)
assert_eq!(model.generate_layout_id(name_3, None), expected);
}
}